package middleware

/*
 * @Desc: 鉴权中间件
 * @author: bsyz
 * @version: v1.0.0
 */

import (
	"errors"
	"github.com/PhlexTheCharr/GoFurry-framework/apps/system/user/models"
	"github.com/PhlexTheCharr/GoFurry-framework/common"
	"github.com/PhlexTheCharr/GoFurry-framework/common/log"
	redis "github.com/PhlexTheCharr/GoFurry-framework/common/service"
	"github.com/PhlexTheCharr/GoFurry-framework/common/util"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"net/http"
	"strings"
	"time"
)

func JWTMiddleWare() gin.HandlerFunc {
	return func(context *gin.Context) {
		authorization := strings.TrimSpace(context.GetHeader("Authorization"))
		if authorization == "" {
			common.NewResponse(context).ErrorWithCode("用户未登录.", http.StatusUnauthorized)
			return
		}
		token := authorization
		cache, err := redis.GetString(token)
		if err == nil && strings.TrimSpace(cache) != "" {
			token = cache
		} else {
			common.NewResponse(context).ErrorWithCode("登录信息已过期.", http.StatusUnauthorized)
			return
		}
		claims, pe := util.ParseToken(token)
		if pe != nil {
			log.Error(pe)
			if errors.Is(pe, jwt.ErrTokenMalformed) {
				common.NewResponse(context).ErrorWithCode("用户登录信息解析失败.", http.StatusUnauthorized)
				return
			} else if errors.Is(pe, jwt.ErrTokenSignatureInvalid) {
				common.NewResponse(context).ErrorWithCode("用户登录信息失效.", http.StatusUnauthorized)
				return
			} else if errors.Is(pe, jwt.ErrTokenExpired) || errors.Is(pe, jwt.ErrTokenNotValidYet) {
				common.NewResponse(context).ErrorWithCode("用户登录信息已过期.", http.StatusUnauthorized)
				return
			} else {
				common.NewResponse(context).ErrorWithCode("用户未登录.", http.StatusUnauthorized)
				return
			}
		}

		// redis续租
		if claims.ExpiresAt.Sub(time.Now()) < common.JWT_RELET_NUM*time.Hour {
			newTokenStr, err := util.NewToken(claims.UserId, claims.UserName)
			if err == nil {
				redis.SetExpire("online_user:"+claims.UserName, claims.UserName, common.ONLINE_RELET_NUM*time.Minute)
				redis.SetExpire(authorization, newTokenStr, common.JWT_RELET_NUM*time.Hour)
			}
		}
		currentId, _ := util.String2Int64(claims.UserId)
		userInfo := models.CurrentUser{
			ID:   currentId,
			Name: claims.UserName,
		}
		context.Set(common.COMMON_AUTH_CURRENT, userInfo)
		context.Next()
	}
}
